package com.anasoft.os.s4j;

import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;

import org.junit.runner.notification.Failure;
import org.junit.runner.notification.RunListener;

import com.anasoft.os.s4j.annotation.ErrorReporting;
import com.anasoft.os.s4j.annotation.SeleniumSession;
import com.thoughtworks.selenium.Selenium;

/**
 * JUnit {@link RunListener} that implements default error reporting behavior of
 * Selenium tests.
 * <p>
 * This listener is configured via the {@link ErrorReporting} annotation.
 * 
 * @see ErrorReporting
 * @see SeleniumSession
 * @see SeleniumRunnerLogic
 * 
 * @author vojtech.szocs
 */
public class ErrorReportingListener extends RunListener {

    private final ErrorReporting errorConfig;
    private final Selenium session;

    /**
     * Creates new run listener for the given error reporting configuration.
     * 
     * @param errorConfig Error reporting configuration.
     * @param session Selenium API for executing commands on the current
     *            session.
     */
    public ErrorReportingListener(ErrorReporting errorConfig, Selenium session) {
        this.errorConfig = errorConfig;
        this.session = session;
    }

    @Override
    public void testFailure(Failure failure) throws Exception {
        try {
            if (errorConfig.enabled()) {
                if (errorConfig.captureScreenshots()) {
                    session.captureScreenshot(getScreenshotFileName(failure, errorConfig.serverOutputDirectory()));
                }

                if (errorConfig.retrieveLastRemoteControlLogs()) {
                    writeFile(getRemoteControlLogsFileName(failure, errorConfig.runnerOutputDirectory()), session.retrieveLastRemoteControlLogs());
                }
            }
        }

        catch (Exception ex) {
            // can't let the Exception fall through, as this would cause this
            // RunListener to be excluded from associated RunNotifier for
            // subsequent tests
            if (!errorConfig.swallowExceptions())
                throw new Error("Error reporting listener execution failure", ex);
        }
    }

    /**
     * Returns path to the screenshot file to be captured upon test failure.
     * 
     * @param failure Description of the failed test.
     * @param outputDirectory Server output directory taken from
     *            {@link ErrorReporting}.
     * @return Path to screenshot file for the failed test.
     */
    protected String getScreenshotFileName(Failure failure, String outputDirectory) {
        return outputDirectory + failure.getDescription().getDisplayName() + ".png";
    }

    /**
     * Returns path to the server log file to be written upon test failure.
     * 
     * @param failure Description of the failed test.
     * @param outputDirectory Runner output directory taken from
     *            {@link ErrorReporting}.
     * @return Path to server log file for the failed test.
     */
    protected String getRemoteControlLogsFileName(Failure failure, String outputDirectory) {
        return outputDirectory + failure.getDescription().getDisplayName() + ".log";
    }

    protected void writeFile(String fileName, String contents) {
        BufferedWriter out = null;

        try {
            out = new BufferedWriter(new FileWriter(fileName));
            out.write(contents);
            out.close();
        } catch (IOException ioex) {
            // ignore I/O errors silently
        } finally {
            if (out != null) {
                try {
                    out.close();
                } catch (IOException ioex) {
                    // can't close the output stream, nothing to do here
                }
            }
        }
    }

}
